package gpl.scotlandyard.beans.basics;

import java.util.Comparator;

/**
 * Link structure contains 2 {@link Node} (from, to), a {@link Ticket} and an hidden {@link Ticket}. <p>As structure
 * suggest link is oriented from one {@link Node} to another.</p> <p>The {@link Ticket} is the type of ticket needed to
 * use this link.</p> <p>The hidden {@link Ticket} is use in case of hidden move. It permits to use a link with hidden
 * {@link Ticket} that takes place of natural ticket and the natural ticket is placed as hidden {@link Ticket}. To get a
 * {@link Link} with hidden ticket we must used factory method createHidden()</p>
 * 
 * @author Norbert Martin
 */
public final class Link {

  private final Node from;
  private final Ticket hidden;
  private final Ticket ticket;
  private final Node to;

  /**
   * Creates the link. The hidden ticket is set to null.
   * 
   * @param from the node
   * @param to the node
   * @param ticket to use
   */
  public Link(Node from, Node to, Ticket ticket) {
    this(from, to, ticket, null);
  }

  /**
   * Creates the link.
   * 
   * @param from the node
   * @param to the node
   * @param ticket to use
   * @param hidden ticket
   */
  private Link(Node from, Node to, Ticket ticket, Ticket hidden) {
    if (hidden != null && ticket != Ticket.HIDE) {
      throw new IllegalArgumentException("ticket must be Ticket.HIDE if hidden is not null");
    }
    this.from = from;
    this.to = to;
    this.ticket = ticket;
    this.hidden = hidden;
  }

  /**
   * Factory method. <p>Creates the same link with hidden ticket as main ticket and natural ticket is set in hidden
   * field.</p>
   * 
   * @return hidden link
   */
  public Link createHidden() {
    return new Link(from, to, Ticket.HIDE, ticket);
  }

  /**
   * Returns the from node of this link.
   * 
   * @return from
   */
  public Node getFromNode() {
    return from;
  }

  /**
   * Returns the hidden ticket of this {@link Link}. It could be null.
   * 
   * @return hidden ticket or null
   */
  public Ticket getHiddenTicket() {
    return hidden;
  }

  /**
   * Gets the type of ticket used for the link.
   * 
   * @return ticket
   */
  public Ticket getTicket() {
    return ticket;
  }

  /**
   * Returns the to node of this link.
   * 
   * @return to
   */
  public Node getToNode() {
    return to;
  }

  @Override
  public String toString() {
    return from + "-->" + ticket + "--> " + to;
  }

  /** Comparator that orders link looking their number and ticket weight.
   * @return
   */
  public static Comparator<Link> toNodeComparator() {
    return new Comparator<Link>() {
      @Override
      public int compare(Link o1, Link o2) {
        int result = o1.getToNode().compareTo(o2.getToNode());
        if (result != 0) {
          return result;
        }

        return o1.getTicket().getWeight() - o2.getTicket().getWeight();
      }
    };
  }

  /** Returns a new link from unknown node to given node with start {@link Ticket}.
   * @param n to
   * @return link
   */
  public static Link buildStart(Node n) {
    return new Link(Node.getUnknownNode(), n, Ticket.START);
  }

  /** Returns a new link from given node to given node with none {@link Ticket}.
   * @param n from / to
   * @return link
   */
  public static Link buildNoMove(Node n) {
    return new Link(n, n, Ticket.NONE);
  }
}
